﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace PractiseThread
{
    class Program
    {

   /***     static void Main(string[] args)
        {
            
        }***/

        static void Test1()
        {
            Parallel.For<string>(0, 20, () =>
            {
                //invoke once for each thread
                Console.WriteLine("init thread {0},task{1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
                return string.Format("t{0}", Thread.CurrentThread.ManagedThreadId);
            },
            (i, pls, str1) =>
            {
                //invoke for each member
                Console.WriteLine("body i {0} str1 {1} thread {2} task {3}", i, str1,
                    Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
                Thread.Sleep(10);
                return string.Format("i {0}", i);
            },
            (str1) =>
            {
                //final action on each thread
                Console.WriteLine("finally {0}", str1);
            });
        }

        static void Test2()
        {
            string[] data = {"zero", "one", "two", "three", "four", "five", "six",
            "seven", "eight", "nine", "ten", "eleven", "twelve" };
            ParallelLoopResult result = Parallel.ForEach<string>(data, (s, pls, l) =>
            {
                Console.WriteLine("{0} {1}", s, l);
            });
        }

        static void Test3()
        {
            Parallel.Invoke(Foo, Bar);
        }

        static void Foo()
        {
            Console.WriteLine("foo");
        }

        static void Bar()
        {
            Console.WriteLine("bar");
        }

        static object taskMethodLock = new object();

        static void TaskMethod(object title)
        {
            lock(taskMethodLock)
            {
                Console.WriteLine(title);
                Console.WriteLine("Task id:{0}, thread:{1}",
                    Task.CurrentId == null ? "no task" : Task.CurrentId.ToString(),
                    Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("is pooled thread: {0}",
                    Thread.CurrentThread.IsThreadPoolThread);
                Console.WriteLine("is background thread:{0}",
                    Thread.CurrentThread.IsBackground);
                Console.WriteLine();
            }
        }

        static void TasksUsingThreadPool()
        {
            var tf = new TaskFactory();
            Task t1 = tf.StartNew(TaskMethod, "using a task factory");

            Task t2 = Task.Factory.StartNew(TaskMethod, "factory via a task");

            var t3 = new Task(TaskMethod, "using a task constructor and Start");
            t3.Start();

            Task t4 = Task.Run(()=> TaskMethod("using the Run method"));
        }

        private static void RunSynchronousTask()
        {
            TaskMethod("just the main thread");
            var t1 = new Task(TaskMethod, "run sync");
            t1.RunSynchronously();
        }

        private static void LongRunningTask()
        {
            var t1 = new Task(TaskMethod, "long running", TaskCreationOptions.LongRunning);
            t1.Start();
        }

        static Tuple<int, int> TaskWithResult(object division)
        {
            Tuple<int, int> div = (Tuple<int, int>)division;
            int result = div.Item1 / div.Item2;
            int reminder = div.Item1 % div.Item2;
            Console.WriteLine("task create a result...");
            return Tuple.Create<int, int>(result, reminder);
        }

        static void DoOnFirst()
        {
            Console.WriteLine("doing some task{0}", Task.CurrentId);
            Thread.Sleep(3000);
        }

        static void DoOnSecond(Task t)
        {
            Console.WriteLine("task {0} finished", t.Id);
            Console.WriteLine("this task id {0}", Task.CurrentId);
            Console.WriteLine("do some cleanup");
            Thread.Sleep(3000);
        }

        static void ParentAndChild()
        {
            var parent = new Task(ParentTask);
            parent.Start();
            Thread.Sleep(2000);
            Console.WriteLine(parent.Status);
            Thread.Sleep(4000);
            Console.WriteLine(parent.Status);
        }

        static void ParentTask()
        {
            Console.WriteLine("task id{0}", Task.CurrentId);
            var child = new Task(ChildTask);
            child.Start();
            Thread.Sleep(1000);
            Console.WriteLine("parent started child");
        }

        static void ChildTask()
        {
            Console.WriteLine("child");
            Thread.Sleep(5000);
            Console.WriteLine("child finished");
        }

        static void CancelTask()
        {
            var cts = new CancellationTokenSource();
            cts.Token.Register(() => Console.WriteLine("*** task cancelled"));
            //send a cancel after 500ms
            cts.CancelAfter(500);

            Task t1 = Task.Run(() =>
            {
                Console.WriteLine("in task");
                for (int i = 0; i < 20; i++)
                {
                    Thread.Sleep(100);
                    CancellationToken token = cts.Token;
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("cancelling was requested," +
                            "cancelling from within the task");
                        token.ThrowIfCancellationRequested();
                        break;
                    }
                    Console.WriteLine("in loop");
                }
                Console.WriteLine("task finished without cancellation");
            },cts.Token);

            try
            {
                t1.Wait();
            }
            catch(AggregateException ex)
            {
                Console.WriteLine("exception: {0}, {1}", ex.GetType().Name, ex.Message);
                foreach(var innerException in ex.InnerExceptions)
                {
                    Console.WriteLine("inner exception:{0} {1}",
                        ex.InnerException.GetType().Name, ex.InnerException.Message);
                }
            }
        }

        static void Test5()
        {
            int nWorkerThreads;
            int nCompletionPortThreads;
            ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads);
            Console.WriteLine("Max worker threader:{0}," + "I/O completion threads:{1}",
                nWorkerThreads, nCompletionPortThreads);
            for(int i = 0; i<5; i++)
            {
                ThreadPool.QueueUserWorkItem(JobForAThread);
            }
            Thread.Sleep(3000);
        }

        static void JobForAThread(object state)
        {
            for(int i = 0; i<3; i++)
            {
                Console.WriteLine("loop {0},running inside pooled thread{1}",
                    i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(50);
            }
        }

        static void Test6()
        {
            var t1 = new Thread(ThreadMain);
            t1.Start();
            Console.WriteLine("This is the main thread.");
        }

        static void ThreadMain()
        {
            Console.WriteLine("Running in a thread.");
        }

        static void Test7()
        {
            var t1 = new Thread(() => Console.WriteLine("running in a thread,id:{0}",
                Thread.CurrentThread.ManagedThreadId));
            t1.Start();
            Console.WriteLine("This is the main thread,id:{0}",
                Thread.CurrentThread.ManagedThreadId);
        }

        public struct Data
        {
            public string Message;
        }

        static void ThreadMainWithParameters(object o)
        {
            Data d = (Data)o;
            Console.WriteLine("Running in a thread,received {0}", d.Message);
        }

        static void Test8()
        {
            var d = new Data { Message = "Info" };
            var t2 = new Thread(ThreadMainWithParameters);
            t2.Start(d);
        }

        public class MyThread
        {
            private string data;

            public MyThread(string data)
            {
                this.data = data;
            }

            public void ThreadMain()
            {
                Console.WriteLine("Running in a thread, data:{0}", data);
            }
        }

        static void Test9()
        {
            var obj = new MyThread("info");
            var t3 = new Thread(obj.ThreadMain);
            t3.Start();
        }

        static void Test10()
        {
            var t1 = new Thread(ThreadMains)
            {
                Name = "MyNewThread",
                IsBackground = false
            };
            t1.Start();
            Console.WriteLine("Main thread ending now.");
        }
        
        static void ThreadMains()
        {
            Console.WriteLine("Thread {0} started", Thread.CurrentThread.Name);
            Thread.Sleep(3000);
            Console.WriteLine("Thread {0} completed", Thread.CurrentThread.Name);
        }

        public class StateObject
        {
            private int state = 5;
            private object sync = new object();

            public void ChangeState(int loop)
            {
                lock (sync)
                {
                    state++;
                    Trace.Assert(state == 6, "Race condition occurred after" + loop + "loops");
                }
                state = 5;
            }
        }

        public class SampleTask
        {
            public SampleTask(StateObject s1, StateObject s2)
            {
                this.s1 = s1;
                this.s2 = s2;
            }

            private StateObject s1;
            private StateObject s2;
            
            public void Deadlock1()
            {
                int i = 0;
                while(true)
                {
                    lock(s1)
                    {
                        lock(s2)
                        {
                            s1.ChangeState(i);
                            s2.ChangeState(i++);
                            Console.WriteLine("still running,{0}", i);
                        }
                    }
                }
            }

            public void Deadlock2()
            {
                int i = 0;
                while(true)
                {
                    lock(s2)
                    {
                        lock(s1)
                        {
                            s1.ChangeState(i);
                            s2.ChangeState(i++);
                            Console.WriteLine("still running, {0}", i);
                        }
                    }
                }
            }

            public void RaceCondition(object o)
            {
                Trace.Assert(o is StateObject, "o must be of type StateObject");
                StateObject state = o as StateObject;
                int i = 0;
                while(true)
                {
                    lock(state)
                    {
                        state.ChangeState(i++);
                    }
                }
            }
        }

 /***      static void RaceConditions()
        {
            var state = new StateObject();
            for (int i = 0; i < 2; i++)
            {
                Task.Run(() => new SampleTask().RaceCondition(state));
            }
        }***/

        public class ShareState
        {
            private int state = 0;
            private object syncRoot = new object();
            public int State
            {
                get
                {
                    return state;
                }
            }

            public int IncrementState()
            {
                lock(syncRoot)
                {
                    return ++state;
                }
            }
        }

   /***     public class Job
        {
            ShareState shareState;
            public Job(ShareState shareState)
            {
                this.shareState = shareState;
            }
            public void DoTheJob()
            {
                for (int i = 0; i < 50000; i++)
                {
                    lock(shareState)
                    {
                        shareState.State += i;
                    }
                }
            }
        }***/

  /***      static void Test11()
        {
            int numTasks = 20;
            var state = new ShareState();
            var tasks = new Task[numTasks];

            for(int i = 0; i < numTasks; i++)
            {
                tasks[i] = Task.Run(() => new Job(state).DoTheJob());
            }

            for(int i = 0; i < numTasks; i++)
            {
                tasks[i].Wait();
            }
            Console.WriteLine("summarized{0}", state.State);
        }***/

        public class Demo
        {
            private class SynchronizedDemo: Demo
            {
                private object syncRoot = new object();
                private Demo d;

                public SynchronizedDemo(Demo d)
                {
                    this.d = d;
                }

                public override bool IsSynchronized
                {
                    get
                    {
                        return true;
                    }
                }

                public override void DoThis()
                {
                   lock(syncRoot)
                    {
                        d.DoThis();
                    }
                }

                public override void DoThat()
                {
                    lock(syncRoot)
                    {
                        d.DoThat();
                    }
                }

            }

            public virtual bool IsSynchronized
            {
                get
                {
                    return false;
                }
            }

            public static Demo Synchronized(Demo d)
            {
                if(!d.IsSynchronized)
                {
                    return new SynchronizedDemo(d);
                }
                return d;
            }

            public virtual void DoThis()
            {

            }

            public virtual void DoThat()
            {

            }
        }


    }
}
